/*
 * ULSF Sagittarius IDA
 * Copyright (C) 2012, John Pritchard, all rights reserved.
 * 
 * This program is free software: you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see
 * <http://www.gnu.org/licenses/>.
 */
package sagittarius.panel;

import sagittarius.design.Arrays;

public class Station
    extends Object
{
    /**
     * Coordinates
     */
    public final float[][] p;
    /**
     * Number of coordinates
     */
    public final int np;
    /**
     * Area
     */
    public final float area;
    /**
     * Centroid
     */
    public final float[] c;


    public Station(float[][] p){
        super();
        if (null != p && 0 < p.length){
            this.p = p;
            this.np = p.length;

            /*
             * Calculate area and centroid
             */
            float area = 0.0f;
            float cy = 0.0f, cz = 0.0f;
            {
                final int zz;
                if (p[0].equals(p[this.np-1]))
                    zz = (this.np-1);
                else
                    zz = (this.np);

                float y0, y1, z0, z1, f;

                for (int cc = 0; cc < zz; cc++){
                    float[] v0 = p[cc];
                    float[] v1 = p[cc+1];

                    y0 = v0[1];
                    y1 = v1[1];

                    z0 = v0[2];
                    z1 = v1[2];

                    f = (z0*y1 - z1*y0);

                    cy += ((y0+y1)*f);

                    cz += ((z0+z1)*f);

                    area += f;
                }
            }

            area /= 2.0;

            this.area = Math.abs(area);

            if (area == area && 0.0 != area){

                area *= 6.0;

                cz /= area;
                cy /= area;
            }
            this.c = new float[]{p[0][0], cy, cz};
        }
        else
            throw new IllegalArgumentException();
    }

    public float getX(){
        return this.p[0][0];
    }
    public float getY(int idx){
        return this.p[idx][1];
    }
    public float getZ(int idx){
        return this.p[idx][2];
    }


    /**
     * For a set of asymmetric half body sections in Z+, produce a set
     * of symmetric full body stations.
     */
    public final static Station[] Translate(Section[] sections){

        final int ns = sections.length;

        final Section sR = sections[0];

        final float xte = sR.getXTE();
        final float xle = sR.getXLE();
        final float dx  = (sR.chord / 1000.0f);
        /*
         * Slice sections into stations
         */
        Station[] stations = null;
        {
            int sec;

            float x = xle;

            float[][][] s;

            while (x <= xte){

                s = null;

                for (sec = 0; sec < ns; sec++){

                    s = Arrays.Add(s,sections[sec].station(x));
                }
                if (null != s){

                    stations = Station.Add(stations,new Station(ToCW(s)));
                }

                x += dx;
            }
        }
        return stations;
    }
    /**
     * Plot station in CW order from root lower to tip to root upper,
     * and then reflect to tip returning to root lower.
     */
    private final static float[][] ToCW(float[][][] s){

        int zz = s.length;
        int mm = (zz>>1);
        if (1 == (zz & 1))
            mm += 1;

        float[] cw[] = null, sec[];
        /*
         * Z+ hemisphere
         */
        for (int cc = 0; cc < zz; cc++){

            sec = s[cc];
            if (null != sec){
                if (cc < mm){
                    /*
                     * Lower
                     */
                    cw = Arrays.Add(cw,sec[0]);
                }
                else if (cc == mm){
                    /*
                     * Tip
                     */
                    if (2 == sec.length){
                        cw = Arrays.Add(cw,sec[0]);
                        cw = Arrays.Add(cw,sec[1]);
                    }
                    else 
                        cw = Arrays.Add(cw,sec[0]);
                }
                else {
                    /*
                     * Upper
                     */
                    if (2 == sec.length)
                        cw = Arrays.Add(cw,sec[1]);
                    else 
                        cw = Arrays.Add(cw,sec[0]);
                }
            }
        }
        /*
         * Z- hemisphere
         */
        for (int cc = (zz-1); -1 < cc; cc--){

            sec = s[cc];
            if (null != sec){
                float[] v;
                if (cc > mm){
                    /*
                     * Upper
                     */
                    if (2 == sec.length)
                        v = sec[1];
                    else 
                        v = sec[0];

                    if (0.0 != v[2])
                        cw = Arrays.Add(cw,(new float[]{v[0],v[1],-(v[2])}));
                    else
                        cw = Arrays.Add(cw,v);
                }
                else if (cc == mm){
                    /*
                     * Tip
                     */
                    if (2 == sec.length){
                        v = sec[1];

                        if (0.0 != v[2])
                            cw = Arrays.Add(cw,(new float[]{v[0],v[1],-(v[2])}));
                        else
                            cw = Arrays.Add(cw,v);

                        v = sec[0];

                        if (0.0 != v[2])
                            cw = Arrays.Add(cw,(new float[]{v[0],v[1],-(v[2])}));
                        else
                            cw = Arrays.Add(cw,v);
                    }
                    else {
                        v = sec[0];

                        if (0.0 != v[2])
                            cw = Arrays.Add(cw,(new float[]{v[0],v[1],-(v[2])}));
                        else
                            cw = Arrays.Add(cw,v);
                    }
                }
                else {
                    /*
                     * Lower
                     */
                    v = sec[0];

                    if (0.0 != v[2])
                        cw = Arrays.Add(cw,(new float[]{v[0],v[1],-(v[2])}));
                    else
                        cw = Arrays.Add(cw,v);
                }
            }
        }
        return cw;
    }

    public final static Station[] Add(Station[] list, Station item){
        if (null == item)
            return list;
        else if (null == list)
            return new Station[]{item};
        else {
            int len = list.length;
            Station[] copier = new Station[len+1];
            System.arraycopy(list,0,copier,0,len);
            copier[len] = item;
            return copier;
        }
    }

}
